package com.license.logic.clonalselection;

import java.io.IOException;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.xml.parsers.ParserConfigurationException;

import org.w3c.dom.DOMException;
import org.xml.sax.SAXException;

import com.license.logic.alimentation.Food;
import com.license.logic.geneticAlgorithm.common.CommonOperation;

public class Operation extends CommonOperation {

//	private static String path;
	private double lambda[] = new double[2];

	public Operation() throws IOException, ParserConfigurationException,
			SAXException {
		lambda[0] = utils.generateRandomDouble(0, 1);
		lambda[1] = 1 - lambda[0];
	}

	/**
	 * selectam toti anticorpii din populatie n=N
	 * */
	public List<Antibody> select(List<Antibody> antibodies, int n) {
		// the list of antibodies are already sorted by individualSharedFitness function, so the last n of them are the best 
		int value = antibodies.size() - n;
		return antibodies.subList(value > 0 ? value : 0 , antibodies.size());
	}
	/**
	 * acei n=N anticorpi selectati for fi generate Nc clone  ,
	 * iar ca iesire avem Cj 
	 * Nc = round(beta*N)
	 * nu avem nevoie neaparat de  afinitati prin urmare numarul de clone pt fiecare
	 * anticorp va fi acelasi 
	 * creez un Map care are ca si cheie un antibody si ca si valoare o lista de antibodies care sunt clonele(copiilesale)
	 * 
	 * */
	public Map<Antibody ,List<Antibody>> cloneSelectedAntibodies(List<Antibody> antibodies, int beta, int antibodyNumber) {
		Map<Antibody ,List<Antibody>> cloneAntibodies =new HashMap<Antibody ,List<Antibody>>();
		int value = 0;
		for(int index = 0 ; index < antibodies.size(); index ++) {
			value = antibodyNumber * beta / ( index + 1 );
			cloneAntibodies.put(antibodies.get(index), this.cloneNAntibody(antibodies.get(index), value));
		}
		return cloneAntibodies;
	}
	
	public List<Antibody> cloneNAntibody(Antibody antibody, int value){
		List<Antibody> antibodies = new ArrayList<Antibody>();
		for (int i = 0 ; i < value ; i++){
			antibodies.add(this.cloneAntibody(antibody));
		}
		return antibodies;
	}

	public void insertAntibodyInOrder(List<Antibody> antibodies, Antibody newAntibody, int antibodyNumber, double sbp, int paretoOptimal) {
		for (int index  = 0 ; index < antibodies.size() ; index ++){
			if (this.individualSharedFitness(antibodies.get(index), antibodyNumber, sbp, paretoOptimal) >
					this.individualSharedFitness(newAntibody,antibodyNumber, sbp, paretoOptimal)){
				antibodies.add(index, newAntibody);
				break;
			}
		}
	}
	
	public void insertAllAntibodiesInOrder(List<Antibody> oldAntibodies, List<Antibody> newAntibodies, int antibodyNumber, double sbp, int paretoOptimal) {
		for (Antibody antibody : newAntibodies){
			this.insertAntibodyInOrder(oldAntibodies, antibody, antibodyNumber, sbp, paretoOptimal);
		}
	}

	public List<Antibody> replace(List<Antibody> antibodies,
			List<Antibody> generatedAntibodies, List<Double> afinitys) {
		return null;
	}

	/**
	 * mutation rule is alpha = exp (-p * f)
	 * alpha = rata de mutatie 
	 * p = controleaza gradul de mutatie (degradare)
	 * f = afinitatile (fitnesul in cazul nostru)
	 * */
	public void hypermut(Map < Antibody, List<Antibody>> antibodiesMap, List<Antibody> selectedAntibodies, int beta, int antibodyNumber) {
		int hypermutValue = 0;
		for (int index = 0 ; index < selectedAntibodies.size(); index ++){
			hypermutValue = antibodyNumber/ (antibodyNumber - index) < 3 ? antibodyNumber/ (antibodyNumber - index) : 3;
			this.hypermutAntibodies(antibodiesMap.get(selectedAntibodies.get(index)), hypermutValue);
		}
		return;
	}
	
	private void hypermutAntibodies(List<Antibody> antibodies, int permuteValue){ 
		// permute an list of Antibodies with random value
		// permuteValue is a number between 1 and 3 an tell us the number of food item that will be generated random in meniu
		for (Antibody antibody : antibodies){
			this.hypermutFoodMeniu(antibody.getAlimentation().getBreakFast(), permuteValue, "breakfast");
			this.hypermutFoodMeniu(antibody.getAlimentation().getDinner(), permuteValue, "dinner");
			this.hypermutFoodMeniu(antibody.getAlimentation().getLunch(), permuteValue, "lunch");
			this.hypermutFoodMeniu(antibody.getAlimentation().getSnack(), permuteValue, "snack");
		}
		
	}
	
	private void hypermutFoodMeniu(List<Food> foods, int permuteValue, String mealType){
		// shuffle the list to get list in random order
		Collections.shuffle(foods);
		List<Food> newFoods = new ArrayList<Food>();
		// keep foods in list
		for (int i = 0 ; i < foods.size() - permuteValue; i++){
			newFoods.add(foods.get(i));
		}
		this.getRandomFoodCopyWithXmlConstraint(foods, this.allFoods.get(mealType),mealType);
	}
	
	
	public List<Antibody> getOrderedListFromMap(Map<Antibody, List<Antibody>> antibodiesMap, int antibodyNumber, double sbp, int paretoOptimal){
		// get antibodies from map and set into a list ordered by fitness value
		List<Antibody> antibodies = new ArrayList<Antibody>();
		for (List<Antibody> arrays : antibodiesMap.values()){
			for (Antibody antibody : arrays){
				this.insertAntibodyInOrder(antibodies, antibody, antibodyNumber, sbp, paretoOptimal);
			}
		}
		return antibodies;
	}

	public List<Antibody> generateRandomAntibodies(int number, double weight, String constraintMethod){
		List<Antibody> antibodies = new ArrayList<Antibody>();
		for (int i = 0 ; i < number ; i++){
			
			Antibody antibody = new Antibody();
			
			antibody.setActivities(this.generateRandomActivities(weight));
			antibody.setAlimentation(this.generateRandomAlimentation(constraintMethod));
			antibody.setWeight(weight);
		}
		return antibodies;
	}

	public double individualSharedFitness(Antibody antibody, int N, double sbp, int paretoOptimal) {
//		double suma = 0;
		double fs = 0;
//		for (int j = 1; j <= N; j++) {
//			suma = suma + share(d);
//		}
//		
//		suma = 1;

		try {
//			fs = fitness(antibody.getAllCalories(), 2, sbp) / suma;
			fs = fitness(antibody.getAllCalories(), paretoOptimal, sbp);

		} catch (DOMException | ParserConfigurationException | SAXException
				| IOException | ParseException e) {
			e.printStackTrace();
		}

		return fs;
	}
	
	

//	private double share(int d) {
//		double sh = 0;
//		if (d < Constants.TRESHOLD_OF_DISIMILARITY) {
//			sh = 1 - (d / Constants.TRESHOLD_OF_DISIMILARITY);
//		}
//		return sh;
//	}
	
	public Antibody cloneAntibody(Antibody oldAntibody){
		Antibody newAntibody = new Antibody();
		newAntibody.setActivities(this.copyActivities(oldAntibody.getActivities()));
		newAntibody.setAlimentation(this.copyAlimentation(oldAntibody.getAlimentation()));
		newAntibody.setWeight(oldAntibody.getWeight());
		return newAntibody;
	}
}
